﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kale.Core.Configuration;
using System.Reflection;
using Kale.Core.Security;

namespace Kale.Core
{
   public static class Factory
   {
      /// <summary>
      /// Creates the object.
      /// </summary>
      /// <typeparam name="T">A type that the specified type will implement.</typeparam>
      /// <param name="type">The type.</param>
      /// <param name="constructorArguments">The constructor arguments.</param>
      /// <returns></returns>
      public static T CreateObject<T>(string type, params object[] constructorArguments)
      {
         string[] parts = type.Split(',');

         try
         {
            if (parts.Length != 2 && parts.Length != 5)
            {
               throw new FactoryException(string.Format("Type string {0} is invalid", type));
            }
            else
            {
               string typeName = parts[0];
               string assemblyName = string.Empty;

               if (parts.Length == 2) assemblyName = parts[1];
               if (parts.Length == 5) assemblyName = parts[1] + ',' + parts[2] + ',' + parts[3] + ',' + parts[4];

               // find the assembly...
               Assembly assembly = Assembly.Load(assemblyName);

               // find the type
               Type objectType = assembly.GetType(typeName, true);

               // construct an instance
               object obj = Activator.CreateInstance(objectType, constructorArguments);

               T castObj = (T)obj;

               return castObj;
            }
         }
         catch (FactoryException)
         {
            throw;
         }
         catch (Exception ex)
         {
            throw new FactoryException(string.Format("An error occurred creating an instance from type string '{0}'", type), ex);
         }
      }

      /// <summary>
      /// Creates the object.
      /// </summary>
      /// <typeparam name="T"></typeparam>
      /// <param name="element">The element.</param>
      /// <returns></returns>
      private static T CreateObject<T>(ITypeSpecificationElement element, Dictionary<string, string> additionalArguments)
      {
         Dictionary<string, string> arguments = element.Arguments.ToDictionary();

         Type targetType = typeof(T);

         // check for custom attribute of RequiresConnectionstring

         RequiresConnectionStringAttribute[] attributes = targetType.GetCustomAttributes(typeof(RequiresConnectionStringAttribute), true) as RequiresConnectionStringAttribute[];

         if (attributes.Length > 0)
         {
            string configurationName = attributes[0].ConfigurationName;

            if (!arguments.ContainsKey(configurationName))
            {
               // add in the default...
               arguments.Add(configurationName, KaleConfigurationSection.Default.DefaultConnectionString);
            }
         }

         if (additionalArguments != null)
         {
            foreach (string key in additionalArguments.Keys)
            {
               if (!arguments.ContainsKey(key))
               {
                  arguments.Add(key, additionalArguments[key]);
               }
            }
         }

         return CreateObject<T>(element.Type, arguments);
      }

      /// <summary>
      /// Creates an album store node provider.
      /// </summary>
      /// <param name="configurationElement">The configuration element.</param>
      /// <returns></returns>
      public static INodeProvider CreateAlbumStoreNodeProvider(AlbumStoreElement configurationElement)
      {
         Dictionary<string,string> additionalArguments = configurationElement.Arguments.ToDictionary();
         additionalArguments.Add("name", configurationElement.Name);

         return CreateObject<INodeProvider>(configurationElement, additionalArguments);
      }

      /// <summary>
      /// Creates a security provider.
      /// </summary>
      /// <param name="configurationElement">The configuration element.</param>
      /// <returns></returns>
      public static ISecurityProvider CreateSecurityProvider(SecurityProviderElement configurationElement)
      {
         return CreateObject<ISecurityProvider>(configurationElement, null);
      }

      /// <summary>
      /// Creates a session provider.
      /// </summary>
      /// <param name="configurationElement">The configuration element.</param>
      /// <returns></returns>
      public static ISessionProvider CreateSessionProvider(SessionProviderElement configurationElement)
      {
         Dictionary<string, string> additionalArguments = configurationElement.Arguments.ToDictionary();
         additionalArguments.Add(SessionProviderElement.AnonymousUsernameConfigurationName, configurationElement.AnonymousUsername);
         additionalArguments.Add(SessionProviderElement.ExpirationTypeConfigurationName, configurationElement.ExpirationType.ToString());
         additionalArguments.Add(SessionProviderElement.ExpirationMinutesConfigurationName, configurationElement.ExpirationMinutes.ToString());

         return CreateObject<ISessionProvider>(configurationElement, additionalArguments);
      }
   }
}
